package chloroform.store

import java.lang.String
import java.net.{URL, URLStreamHandler, URLConnection}
import java.io.{ByteArrayOutputStream, ByteArrayInputStream}
import com.google.appengine.api.datastore._
import chloroform.{ClasspathResourceResolver, Resource, ResourceResolver}
import util.StreamUtils
import util.logging.Logged

/**
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: 20.3.11
 * Time: 18:26
 * To change this template use File | Settings | File Templates.
 */

@serializable
@SerialVersionUID(1)
object DataStoreProjectResourceResolver {
  val ProjectEntityName: String = "CloudioProject"
  val ProjectDefinitionProperty: String = "definition"
  val utf8: String = "utf-8"
}

import DataStoreProjectResourceResolver._

@SerialVersionUID(1)
class DataStoreProjectResourceResolver(val fallbackResolver: ResourceResolver) extends ResourceResolver with Logged {

  def this() = this (new ClasspathResourceResolver(classOf[DataStoreProjectResourceResolver]))

  private def fallbackResolve(resource: String): ProjectEntityResource = {
    debug("Project not found in datastore: " + resource)

    // try to locate the resource via the fallback resolver
    val fallbackProjectResource = fallbackResolver.resolve(resource)

    val newProjectEntity = new Entity(ProjectEntityName, resource)
    val projectFileContent: String = StreamUtils.readAsString(fallbackProjectResource.openInputStream, utf8)

    val definition = new Text(projectFileContent)
    newProjectEntity.setProperty(ProjectDefinitionProperty, definition)

    new ProjectEntityResource(newProjectEntity, resource)
  }

  def resolve(resource: String) = {
    val projectKey = KeyFactory.createKey(ProjectEntityName, resource)

    val datastore = DatastoreServiceFactory.getDatastoreService()
    try {
      val projectEntity = datastore.get(projectKey)
      debug("Project found in datastore: " + projectKey)
      new ProjectEntityResource(projectEntity, resource)
    }
    catch {
      case notFound: EntityNotFoundException => {
        fallbackResolve(resource)
      }
    }
  }
}

@SerialVersionUID(1)
class ProjectEntityResource(projectEntity: Entity, val resourceName: String) extends Resource with Logged {

  def openInputStream = {
    val definition = projectEntity.getProperty(ProjectDefinitionProperty).asInstanceOf[Text]
    val definitionContent = definition.getValue
    new ByteArrayInputStream(definitionContent.getBytes(utf8))
  }

  def openOutputStream = {
    new ByteArrayOutputStream {
      override def close = {
        super.close

        val datastore = DatastoreServiceFactory.getDatastoreService()
        val txn = datastore.beginTransaction()
        try {
          val content = toByteArray
          val stringContent: String = new String(content, utf8)
          projectEntity.setProperty(ProjectDefinitionProperty, new Text(stringContent))

          datastore.put(projectEntity)

          txn.commit()

          debug("Storing entity for project " + resourceName + ". Definition " + stringContent)
        } finally {
          if (txn.isActive()) {
            txn.rollback()
          }
        }
      }
    }
  }
}